package main;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.ArrayList;
import java.util.Random;

import java.awt.*;
import java.awt.event.*;

public class Battle {
	
	public static UI myUI = null;
	public static Battle currBattle = null;
	
	public static Random rng = new Random();
	
	public ArrayList<Unit> goodUnits;
	public ArrayList<Unit> badUnits;
	
	public ArrayList<Unit> losers;
	public ArrayList<Unit> winners;
	
	public int majority = -1;
	public int victor = -1;
	
	public Battle() {
		goodUnits = new ArrayList<Unit>();
		badUnits = new ArrayList<Unit>();
	}
	
	public void addUnit(Unit u) {
		if (u.army == 0)
			this.goodUnits.add(u);
		else
			this.badUnits.add(u);
		if (goodUnits.size() > 1)
			majority = 0;
		else if (badUnits.size() > 1)
			majority = 1;
	}
	
	public void removeUnit(Unit u) {
		if (u.army == 0)
			this.goodUnits.remove(u);
		else
			this.badUnits.remove(u);
		if (this.goodUnits.size() <= 0) {
			Game.battles.remove(this);
			for (Unit t : badUnits) {
				t.battle = null;
			}
		}
		if (this.badUnits.size() <= 0) {
			Game.battles.remove(this);
			for (Unit t : goodUnits) {
				t.battle = null;
			}
		}
	}
	
	public void resolve(){
		currBattle = this;
		
		myUI.getByName("Fight").setEnabled(false);
		myUI.getByName("End Phase").setEnabled(false);
		
		// Set up choices for units involved
		Choice goodChoice = new Choice();
		Choice badChoice = new Choice();
		goodChoice.setName("goodChoice");
		badChoice.setName("badChoice");
		for(Unit u : this.goodUnits) {
			goodChoice.add(u.toChoiceString());
		}
		for(Unit u : this.badUnits) {
			badChoice.add(u.toChoiceString());
		}
		goodChoice.setLocation(myUI.dim.width/2 - 100, myUI.dim.height - 70);
		badChoice.setLocation(myUI.dim.width/2, myUI.dim.height - 70);
		Unit goodUnit = goodUnits.get(0);
		int count = 0;
		for (Equipment equip : goodUnit.equipmentList) {
			if (equip.options != null) {
				equip.options.setLocation(myUI.dim.width/2 - 200, myUI.dim.height - 70 + 20*count);
				myUI.add(equip.options);
				count += 1;
			}
		}
		Unit badUnit = badUnits.get(0);
		count = 0;
		for (Equipment equip : badUnit.equipmentList) {
			if (equip.options != null) {
				equip.options.setLocation(myUI.dim.width/2 + 100, myUI.dim.height - 70 + 20*count);
				myUI.add(equip.options);
				count += 1;
			}
		}
		ItemListener i = new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				myUI.removeByName("Equipment Options");
				Unit goodUnit = goodUnits.get(((Choice)myUI.getByName("goodChoice")).getSelectedIndex());
				int count = 0;
				for (Equipment equip : goodUnit.equipmentList) {
					if (equip.options != null) {
						equip.options.setLocation(myUI.dim.width/2 - 200, myUI.dim.height - 70 + 20*count);
						myUI.add(equip.options);
						count += 1;
					}
				}
				Unit badUnit = badUnits.get(((Choice)myUI.getByName("badChoice")).getSelectedIndex());
				count = 0;
				for (Equipment equip : badUnit.equipmentList) {
					if (equip.options != null) {
						equip.options.setLocation(myUI.dim.width/2 + 100, myUI.dim.height - 70 + 20*count);
						myUI.add(equip.options);
						count += 1;
					}
				}
			}
		};
		goodChoice.addItemListener(i);
		badChoice.addItemListener(i);
		myUI.add(goodChoice);
		myUI.add(badChoice);
		
		
		Button rollFightDice = new Button("Ready to fight");
		rollFightDice.setName("Roll fight dice");
		rollFightDice.setBounds(myUI.dim.width-100, 100, 100, 50);
		rollFightDice.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// Apply the chosen options
				for (Unit u : goodUnits) {
					u.battleDice = u.attacks;
					u.battleBonus = 0;
					u.woundDice = u.attacks;
					u.woundBonus = 0;
					for (Equipment equip : u.equipmentList) {
						equip.applyOptions();
					}
				}
				for (Unit u : badUnits) {
					u.battleDice = u.attacks;
					u.battleBonus = 0;
					u.woundDice = u.attacks;
					u.woundBonus = 0;
					for (Equipment equip : u.equipmentList) {
						equip.applyOptions();
					}
				}
				// Clean up choices
				myUI.removeByName("Equipment Options");
				myUI.removeByName("goodChoice");
				myUI.removeByName("badChoice");
				myUI.removeByName("Roll fight dice");
				
				// Move to next stage
				currBattle.rollFightDice();
			}
		});
		myUI.add(rollFightDice);
		
		myUI.revalidate();
		myUI.repaint();
	}
	
	public void rollFightDice() {
		ArrayList<Integer> goodRolls = new ArrayList<Integer>();
		ArrayList<Integer> badRolls = new ArrayList<Integer>();
		int goodFV = 0;
		int badFV = 0;
		// Roll the dice
		for (Unit u : goodUnits) {
			if (u.fightValue > goodFV)
				goodFV = u.fightValue;
			for (int i = 0; i < u.battleDice+Math.min(1, u.supporters.size()); i++) {
				int roll = rng.nextInt(6) + 1;
				roll += u.battleBonus;
				goodRolls.add(Math.max(1, Math.min(6, roll)));
			}
		}
		for (Unit u : badUnits) {
			System.out.println(u.supporters.size());
			if (u.fightValue > badFV)
				badFV = u.fightValue;
			for (int i = 0; i < u.battleDice+Math.min(1, u.supporters.size()); i++) {
				int roll = rng.nextInt(6) + 1;
				roll += u.battleBonus;
				badRolls.add(Math.max(1, Math.min(6, roll)));
			}
		}
		System.out.println("Good rolls, fightValue: " + goodRolls.toString() + ", " + goodFV);
		System.out.println("Bad rolls, fightValue: " + badRolls.toString() + ", " + badFV);
		
		int goodMax = Collections.max(goodRolls);
		int badMax = Collections.max(badRolls);
		
		if (goodMax > badMax || (goodMax == badMax && goodFV > badFV) ||
				(goodMax == badMax && goodFV == badFV && rng.nextInt(2) == 0)) {
			System.out.println("Good wins");
			this.victor = 0;
			this.winners = this.goodUnits;
			this.losers = this.badUnits;
		}
		else {
			System.out.println("Bad wins");
			this.victor = 1;
			this.winners = this.badUnits;
			this.losers = this.goodUnits;
		}
		
		for (Unit u : this.losers){
			u.moveLeft = 1;
			for (Unit ally : Game.armies[(this.victor+1)%2].units) {
				if (Utils.inchDist(u, ally) < 2 && ally.battle == null) {
					ally.moveLeft = 1;
				}
			}
		}
		
		Button doneRetreating = new Button("Done Retreating");
		doneRetreating.setName("Done Retreating");
		doneRetreating.setBounds(myUI.dim.width-100, 100, 100, 50);
		doneRetreating.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				for (Unit u : currBattle.losers) {
					if (u.moveLeft > 0.2) {
						u.hasRetreated = false;
						System.out.println("No retreat");
					}
				}
				myUI.removeByName("Done Retreating");
				currBattle.dealWounds();
			}
		});
		myUI.add(doneRetreating);
		
		myUI.revalidate();
		myUI.repaint();
		
		
	}
	
	public void dealWounds() {
		Choice attackers = new Choice();
		Choice targets = new Choice();
		attackers.setName("attackers");
		targets.setName("targets");
		attackers.setLocation(myUI.dim.width/2 - 150, myUI.dim.height - 70);
		targets.setLocation(myUI.dim.width/2 + 50, myUI.dim.height - 70);
		Label attacks = new Label("attacks");
		attacks.setName("attacks");
		attacks.setBounds(myUI.dim.width/2 - 50, myUI.dim.height - 70,100,20);
		
		
		for (Unit u : this.winners) {
			for (int i = 0; i < u.woundDice+Math.min(1, u.supporters.size()); i++) {
				attackers.add(u.toChoiceString());
			}
		}
		for (Unit u : this.losers) {
			for (int i = 0; i < u.wounds; i++) {
				if (u.hasRetreated)
					targets.add(u.toChoiceString());
				else
					targets.add(u.toChoiceString() + "-(T)");
			}
		}
		
		
		Button strike = new Button("Strike");
		strike.setName("Strike");
		strike.setBounds(myUI.dim.width-100, 100, 100, 50);
		strike.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Choice attackers = (Choice)myUI.getByName("attackers");
				Choice targets = (Choice)myUI.getByName("targets");
				
				Unit attacker = currBattle.getSelectedUnit(attackers);
				Unit target = currBattle.getSelectedUnit(targets);
				
				// Strike
				attacker.strike(target);
				if (target.hasRetreated == false)
					attacker.strike(target);
				
				// Remove attacker
				attackers.remove(attackers.getSelectedIndex());
				
				// Check for end conditions
				if (attackers.getItemCount() == 0 || targets.getItemCount() == 0) {
					currBattle.cleanUp();
				}
			}
		});
		
		
		myUI.add(strike);
		myUI.add(attackers);
		myUI.add(targets);
		myUI.add(attacks);
		myUI.revalidate();
		myUI.repaint();
		
		if (attackers.getItemCount() == 0 || targets.getItemCount() == 0) {
			this.cleanUp();
			return;
		}
	}
	
	public void refreshWinnersLosers(Choice attackers, Choice targets) {
		attackers.removeAll();
		targets.removeAll();
		for (Unit u : this.winners) {
			for (int i = 0; i < u.woundDice+Math.min(1, u.supporters.size()); i++) {
				attackers.add(u.toChoiceString());
			}
		}
		for (Unit u : this.losers) {
			for (int i = 0; i < u.wounds; i++) {
				if (u.hasRetreated)
					targets.add(u.toChoiceString());
				else
					targets.add(u.toChoiceString() + "-(T)");
			}
		}
	}
	
	public void cleanUp() {
		// Deal with dead people
		for (Unit u : goodUnits) {
			if (u.wounds <= 0) {
				Game.armies[0].units.remove(u);
			}
		}
		for (Unit u : badUnits) {
			if (u.wounds <= 0) {
				Game.armies[1].units.remove(u);
			}
		}
		
		// no one's in a fight anymore
		Game.battles.remove(this);
		for (Unit u : goodUnits)
			u.battle = null;
		for (Unit u : badUnits)
			u.battle = null;
		
		// reset everyone's moves to 0
		for (Army a : Game.armies) {
			for (Unit u : a.units){
				u.moveLeft = 0;
			}
		}

		// Clean up everything, and re enable buttons
		myUI.removeByName("attackers");
		myUI.removeByName("targets");
		myUI.removeByName("attacks");
		myUI.removeByName("Strike");
		
		myUI.getByName("End Phase").setEnabled(true);
		myUI.getByName("Fight").setEnabled(true);
		
		Battle.currBattle = null;
		myUI.revalidate();
		myUI.repaint();
	}
	
	public Unit getSelectedUnit(Choice c) {
		int id = Integer.parseInt(c.getSelectedItem().split("-")[1]);
		for (Unit u : goodUnits) {
			if (u.id == id)
				return u;
		}
		for (Unit u : badUnits) {
			if (u.id == id)
				return u;
		}
		return null;
	}
	
	public String toString() {
		String s = "";
		s += "Good- ";
		for (Unit u : goodUnits){
			s += u.name + " " + u.id + " ";
		}
		s += "Bad- ";
		for (Unit u : badUnits){
			s += u.name + " " + u.id + " ";
		}
		return s;
	}
	
}